// Written in the D Programming Language
module glc_test;

import derelict.opengl.gl;
import derelict.opengl.glu;
import derelict.opengl.glc;
import derelict.sdl.sdl;

import tango.stdc.stringz;

/// The window title
const char[] WINDOW_TITLE = "GLC Test";

/// The main loop flag
bool running;

/**
 * Module constructor. Here we load the GL, GLU and SDL shared libraries,
 * and the initialize SDL.
 */
static this()
{
	DerelictGL.load();
	DerelictGLU.load();
	DerelictGLC.load();
	DerelictSDL.load();

	if(SDL_Init(SDL_INIT_VIDEO) < 0)
	{
		throw new Exception("Failed to initialize SDL: " ~ getSDLError());
	}
}

/**
 * Module destructor. SDL_Quit must be called somewhere, and as we initialized
 * it in the module constructor so the module destructor should be a suitable
 * place.
 */
static ~this()
{
	SDL_Quit();
}

/**
 * The main function. This is where the fun begins. The first order of business
 * is the check the command line arguments if the user wanted to start in
 * fullscreen mode. Then the window is created and OpenGL is initialized with
 * basic settings. Finally the the function starts the main loop which will live
 * for the duration of the application.
 *
 * Params:
 *      args = the command line arguments
 */
void main(char[][] args)
{
	createGLWindow(WINDOW_TITLE, 640, 480, 16, false);
	initGL();

	running = true;
	while(running)
	{
		processEvents();

		drawGLScene();

		SDL_GL_SwapBuffers();
		SDL_Delay(10);
	}
}

/**
 * Process all the pending events.
 */
void processEvents()
{
	SDL_Event event;
	while(SDL_PollEvent(&event))
	{
		switch(event.type)
		{
			case SDL_KEYUP:
				keyReleased(event.key.keysym.sym);
				break;
			case SDL_QUIT:
				running = false;
				break;
			default:
				break;
		}
	}
}

/**
 * Process a key released event.
 */
void keyReleased(int key)
{
	switch(key)
	{
		case SDLK_ESCAPE:
			running = false;
			break;
		default:
			break;
	}
}

/**
 * Resize and initialize the OpenGL window.
 */
void resizeGLScene(GLsizei width, GLsizei height)
{
	// Reset The Current Viewport
	glViewport(0, 0, width, height);

	// Select The Projection Matrix
	glMatrixMode(GL_PROJECTION);

	// Reset The Projection Matrix
	glLoadIdentity();

	// Set our ortho view
	glOrtho(0.0f, width, height, 0.0f, -1.0f, 1.0f);

	// Select The Modelview Matrix
	glMatrixMode(GL_MODELVIEW);

	// Reset The Modelview Matrix
	glLoadIdentity();
}

/**
 * Initializes the GLC context and creates a font.
 */
void initGLC()
{
	glcContext(glcGenContext());
	GLint myFont = glcGenFontID();
	version(Windows)
	{
		glcNewFontFromFamily(myFont, toStringz("Arial"));
	}
	else
	{
		glcNewFontFromFamily(myFont, toStringz("Helvetica"));
	}
	glcFont(myFont);
	glcScale(64.0f, 64.0f);
}

/**
 * Initialize OpenGL.
 */
void initGL()
{
	// Enables Smooth Shading
	glShadeModel(GL_SMOOTH);
	// Black Background
	glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
	// Depth Buffer Setup
	glClearDepth(1.0f);
	// Really Nice Perspective Calculations
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	// Initialize the GLC
	initGLC();
}

/**
 * The drawing function. Now we only clear the color and depht buffers, so that
 * the window stays black.
 */
void drawGLScene()
{
	// Clear The Screen And The Depth Buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// Reset The Current Modelview Matrix
	glLoadIdentity();

	// Set the color to red
	glColor3f(1.0f, 0.0f, 0.0f);
	// Move to about the center of the screen
	glRasterPos2f(200.0f, 200.0f);
	// Draw the text
	glcRenderString(toStringz("Hello, world!"));
}

/**
 * Initializes and opens the SDL window.
 */
void createGLWindow(char[] title, int width, int height, int bits, bool fullScreen)
{
	// Set the OpenGL attributes
	SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 5);
	SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 6);
	SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 5);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	// Set the window title
	SDL_WM_SetCaption(toStringz(title), null);

	// Note the SDL_DOUBLEBUF flag is not required to enable double
	// buffering when setting an OpenGL video mode.
	// Double buffering is enabled or disabled using the
	// SDL_GL_DOUBLEBUFFER attribute. (See above.)
	int mode = SDL_OPENGL;
	if(fullScreen)
	{
		mode |= SDL_FULLSCREEN;
	}
	// Now open a SDL OpenGL window with the given parameters
	if(SDL_SetVideoMode(width, height, bits, mode) is null)
	{
		throw new Exception("Failed to open OpenGL window: " ~ getSDLError());
	}

	resizeGLScene(width, height);
}

/**
 * Get the SDL error as a D string.
 *
 * Returns: A D string containing the current SDL error.
 */
char[] getSDLError()
{
	return fromStringz(SDL_GetError());
}
